#ifndef AGXV_PRIVATE_H
#define AGXV_PRIVATE_H

#include "vk_instance.h"
#include "vk_physical_device.h"
#include "vk_device.h"
#include "vk_util.h"
#include "vk_alloc.h"
#include "vk_log.h"

#include "nir.h"

#include "agxv_entrypoints.h"

#include "pool.h"
#include "agx_bo.h"
#include "agx_device.h"

struct agxv_instance;

struct agxv_physical_device {
    struct vk_physical_device vk;
    struct agx_device dev;
};

struct agxv_instance {
    struct vk_instance vk;

    int physicalDeviceCount;
    struct agxv_physical_device physicalDevice;
};

struct agxv_device {
    struct vk_device vk;

    struct agxv_instance *instance;
    struct agxv_physical_device *pdevice;

    //    struct agxv_queue queue;
};

struct agxv_queue {
    struct vk_device vk;
};

struct agxv_queue_submit_wait_info {
};

struct agxv_device_memory {
   struct vk_object_base base;

   struct agx_bo *bo;
};

struct agxv_buffer {
   struct vk_object_base base;

   VkDeviceSize size;

   VkBufferUsageFlags usage;
   VkBufferCreateFlags flags;

//    struct agx_bo *bo;
//    VkDeviceSize bo_offset;
};

struct agxv_buffer_view {
    struct vk_object_base base;

    struct agxv_buffer *buffer;
};

#define AGXV_MAX_MIP_LEVELS 13

struct agxv_image {
    struct vk_object_base base;

    uint64_t drm_format_mod;

    VkImageType type;
    VkImageAspectFlags aspects;

    VkExtent3D extent;
    uint32_t levels;


    VkImageUsageFlags usage;
    VkImageCreateFlags flags;
    VkImageTiling tiling;

    VkFormat vk_format;

    /* Hardware backing */
    struct agx_bo *bo;

    //    BITSET_DECLARE(data_valid, PIPE_MAX_TEXTURE_LEVELS);

    struct {
        unsigned offset;
        unsigned line_stride;
    } slices[AGXV_MAX_MIP_LEVELS];
};

struct agxv_image_view {
    struct vk_object_base base;

    struct agxv_image *image;
    VkImageAspectFlags aspects;
};

struct agxv_subpass_attachment {
   uint32_t attachment;
   VkImageLayout layout;
};

struct agxv_subpass {
    uint32_t input_count;
    struct agxv_subpass_attachment *input_attachments;

    uint32_t color_count;
    struct agxv_subpass_attachment *color_attachments;
    struct agxv_subpass_attachment *resolve_attachments;

    struct agxv_subpass_attachment ds_attachment;

    /* If we need to emit the clear of the depth/stencil attachment using a
    * a draw call instead of using the TLB (GFXH-1461).
    */
    bool do_depth_clear_with_draw;
    bool do_stencil_clear_with_draw;

    /* Multiview */
    uint32_t view_mask;
};

struct agxv_render_pass_attachment {
    VkAttachmentDescription desc;
    uint32_t first_subpass;
    uint32_t last_subpass;
};

struct agxv_render_pass {
    struct vk_object_base base;

    bool multiview_enabled;

    uint32_t attachment_count;
    struct agxv_render_pass_attachment *attachments;

    uint32_t subpass_count;
    struct agxv_subpass *subpasses;

    struct agxv_subpass_attachment *subpass_attachments;
};

struct agxv_sampler {
    struct vk_object_base base;
    VkSamplerCreateInfo create_info;
    // union pipe_color_union border_color;
    VkSamplerReductionMode reduction_mode;
    uint32_t state[4];
};

struct agxv_framebuffer {
   struct vk_object_base base;

   uint32_t width;
   uint32_t height;
   uint32_t layers;

   uint32_t attachment_count;
   uint32_t color_attachment_count;
   struct agxv_image_view *attachments[0];
};

struct agxv_descriptor_pool {
   struct vk_object_base base;
   VkDescriptorPoolCreateFlags flags;
   uint32_t max_sets;

   struct list_head sets;
};

struct agxv_descriptor_set_binding_layout {
    uint16_t descriptor_index;
    /* Number of array elements in this binding */
    VkDescriptorType type;
    uint16_t array_size;
    bool valid;

    int16_t dynamic_index;
    struct {
        int16_t const_buffer_index;
        int16_t shader_buffer_index;
        int16_t sampler_index;
        int16_t sampler_view_index;
        int16_t image_index;
    } stage[MESA_SHADER_STAGES];

    /* Immutable samplers (or NULL if no immutable samplers) */
    struct agxv_sampler **immutable_samplers;
};

struct agxv_descriptor_set_layout {
    struct vk_object_base base;

    const VkAllocationCallbacks *alloc;
    /* Descriptor set layouts can be destroyed at almost any time */
    uint32_t ref_cnt;

    /* Number of bindings in this descriptor set */
    uint16_t binding_count;

    /* Total size of the descriptor set with room for all array entries */
    uint16_t size;

    /* Shader stages affected by this descriptor set */
    uint16_t shader_stages;

    /* Number of dynamic offsets used by this descriptor set */
    uint16_t dynamic_offset_count;

    /* Bindings in this descriptor set */
    struct agxv_descriptor_set_binding_layout binding[0];
};

struct agxv_pipeline_layout
{
    struct vk_object_base base;
    struct {
        struct agxv_descriptor_set_layout *layout;
        uint32_t dynamic_offset_start;
    } set[8];

    uint32_t num_sets;
    uint32_t push_constant_size;
    struct {
        bool has_dynamic_offsets;
    } stage[MESA_SHADER_STAGES];
};

union agxv_descriptor_info {
   struct {
      struct agxv_sampler *sampler;
      struct agxv_image_view *iview;
      VkImageLayout image_layout;
   };
   struct {
      struct agxv_buffer *buffer;
      VkDeviceSize offset;
      VkDeviceSize range;
   };
   struct agxv_buffer_view *buffer_view;
};

struct agxv_descriptor {
   VkDescriptorType type;
   union agxv_descriptor_info info;
};

struct agxv_descriptor_set {
   struct vk_object_base base;
   struct agxv_descriptor_set_layout *layout;
   struct list_head link;
   struct agxv_descriptor descriptors[0];
};
struct agxv_cmd_pool {
    struct vk_object_base base;

    VkAllocationCallbacks alloc;

    struct list_head cmd_buffers;
    struct list_head free_cmd_buffers;
};

#define MAX_VIEWPORTS 1
#define MAX_SCISSORS  1

struct agxv_viewport_state
{
    uint32_t count;
    VkViewport viewports[MAX_VIEWPORTS];
    float translate[MAX_VIEWPORTS][3];
    float scale[MAX_VIEWPORTS][3];
};

struct agxv_scissor_state
{
    uint32_t count;
    VkRect2D scissors[MAX_SCISSORS];
};

/* Mostly a agxv mapping of VkDynamicState, used to track which data as
 * defined as dynamic
 */
enum agxv_dynamic_state_bits {
   AGXV_DYNAMIC_VIEWPORT                  = 1 << 0,
   AGXV_DYNAMIC_SCISSOR                   = 1 << 1,
   AGXV_DYNAMIC_STENCIL_COMPARE_MASK      = 1 << 2,
   AGXV_DYNAMIC_STENCIL_WRITE_MASK        = 1 << 3,
   AGXV_DYNAMIC_STENCIL_REFERENCE         = 1 << 4,
   AGXV_DYNAMIC_BLEND_CONSTANTS           = 1 << 5,
   AGXV_DYNAMIC_DEPTH_BIAS                = 1 << 6,
   AGXV_DYNAMIC_LINE_WIDTH                = 1 << 7,
   AGXV_DYNAMIC_ALL                       = (1 << 8) - 1,
};

struct agxv_dynamic_state {
    /**
    * Bitmask of (1 << VK_DYNAMIC_STATE_*).
    * Defines the set of saved dynamic state.
    */
    uint32_t mask;

    struct agxv_viewport_state viewport;

    struct agxv_scissor_state scissor;

    struct {
        uint32_t front;
        uint32_t back;
    } stencil_compare_mask;

    struct {
        uint32_t front;
        uint32_t back;
    } stencil_write_mask;

    struct {
        uint32_t front;
        uint32_t back;
    } stencil_reference;

    float blend_constants[4];

    struct {
        float constant_factor;
        float depth_bias_clamp;
        float slope_factor;
    } depth_bias;

    float line_width;
};

struct agxv_pipeline_cache {
    struct vk_object_base base;
};

struct agxv_pipeline {
   struct vk_object_base base;

   struct agxv_device *device;

   struct agxv_dynamic_state dynamic_state;

   nir_shader *pipeline_nir[MESA_SHADER_STAGES];
};

struct agx_scissors {
    struct agx_bo *bo;
    unsigned count;
};

struct agxv_batch {
    struct list_head list;

    unsigned width, height, nr_cbufs;
    //    struct pipe_surface *cbufs[8];
    //    struct pipe_surface *zsbuf;
    struct agxv_image_view *cbuf[8];
    struct agxv_image_view *zsbuf;

    /* PIPE_CLEAR_* bitmask */
    uint32_t clear, draw;

    float clear_color[4];

    /* Resource list requirements, represented as a bit set indexed by BO
    * handles (GEM handles on Linux, or IOGPU's equivalent on macOS) */
    BITSET_WORD bo_list[256];

    struct agx_pool pool, pipeline_pool;
    struct agx_bo *encoder;
    uint8_t *encoder_current;

    struct agx_scissors scissor;
};

struct agxv_cmd_pipeline_state {
    struct agxv_pipeline *pipeline;

    //    struct agxv_descriptor_state descriptor_state;
};

struct agxv_cmd_buffer_state {
    struct agxv_batch *batch;

    struct agxv_render_pass *render_pass;
    struct agxv_framebuffer *framebuffer;
    struct agxv_cmd_pipeline_state gfx;
};

struct agx_job {
    struct list_head list;
    struct agxv_batch batch;
    struct agx_bo cmdbuf, memmap;
};

struct agxv_cmd_buffer {
    struct vk_object_base base;

    struct agxv_device *device;

    struct agxv_cmd_pool *pool;
    struct list_head pool_link;

    struct agxv_cmd_buffer_state state;

    unsigned id;

    struct list_head render_job;

    VkCommandBufferLevel level;
};

#define AGXV_DEFINE_HANDLE_CASTS(__agxv_type, __VkType)                           \
                                                                                    \
    static inline struct __agxv_type *__agxv_type##_from_handle(__VkType _handle) \
    {                                                                               \
        return (struct __agxv_type *)_handle;                                      \
    }                                                                               \
                                                                                    \
    static inline __VkType __agxv_type##_to_handle(struct __agxv_type *_obj)      \
    {                                                                               \
        return (__VkType)_obj;                                                      \
    }

#define AGXV_DEFINE_NONDISP_HANDLE_CASTS(__agxv_type, __VkType)                   \
                                                                                    \
    static inline struct __agxv_type *__agxv_type##_from_handle(__VkType _handle) \
    {                                                                               \
        return (struct __agxv_type *)(uintptr_t)_handle;                           \
    }                                                                               \
                                                                                    \
    static inline __VkType __agxv_type##_to_handle(struct __agxv_type *_obj)      \
    {                                                                               \
        return (__VkType)(uintptr_t)_obj;                                           \
    }

#define AGXV_FROM_HANDLE(__agxv_type, __name, __handle) \
    struct __agxv_type *__name = __agxv_type##_from_handle(__handle)

AGXV_DEFINE_HANDLE_CASTS(agxv_cmd_buffer, VkCommandBuffer)
AGXV_DEFINE_HANDLE_CASTS(agxv_device, VkDevice)
AGXV_DEFINE_HANDLE_CASTS(agxv_instance, VkInstance)
AGXV_DEFINE_HANDLE_CASTS(agxv_physical_device, VkPhysicalDevice)
AGXV_DEFINE_HANDLE_CASTS(agxv_queue, VkQueue)

AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_cmd_pool, VkCommandPool)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_buffer, VkBuffer)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_buffer_view, VkBufferView)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_descriptor_pool, VkDescriptorPool)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_descriptor_set, VkDescriptorSet)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_descriptor_set_layout,
                                  VkDescriptorSetLayout)
// AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_descriptor_update_template,
//                                   VkDescriptorUpdateTemplate)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_device_memory, VkDeviceMemory)
// AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_event, VkEvent)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_framebuffer, VkFramebuffer)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_image, VkImage)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_image_view, VkImageView);
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_pipeline_cache, VkPipelineCache)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_pipeline, VkPipeline)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_pipeline_layout, VkPipelineLayout)
// AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_query_pool, VkQueryPool)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_render_pass, VkRenderPass)
AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_sampler, VkSampler)
// AGXV_DEFINE_NONDISP_HANDLE_CASTS(agxv_sampler_ycbcr_conversion, VkSamplerYcbcrConversion)

#endif